#pragma once

#include <boostext/signal.hpp>
#include <logger/memorystream.hpp>
#include <services/future.hpp>

#include "DebuggerConnection.hpp"




namespace Engine
{
	namespace Edit
	{
		class Debugger
			: public QObject
		{
			Q_OBJECT

		public:

			SHARED(Debugger);

			typedef boost::signals2::signal<void()>   notify;

			/**
			 * This structure/enum defines all possible states for the debugger.
			 */
			struct State
			{
				enum Type
				{
					// Accepted States over the lifetime
					Starting, //< The debugger is waiting for the child process to start
					Attaching, //< The debugger is waiting for the connection to the process to be established
					Running, //< The child process is running and the debugger is fully attached
					Paused, //< The child process is paused (by the other side of the debugger)

					WaitingForResponse, //< The debugger is waiting for a response to a cruical command: no other command can be sent
					Idle, //< No debugging
				};
			};

		public:

			/**
			 * Creates a new debugger that starts the given process on this machine
			 * and attaches itself to it.
			 */
			Debugger(QFileInfo process, quint16 port, logger::memorystream::ptr logger);
			virtual ~Debugger();



			/**
			 * @returns the current state of the debugger
			 */
			State::Type state() const;

			/**
			 * Tests if the debugger is already attached to the process.
			 */
			bool isAttached() const;

			/**
			 * Continues the attached process.
			 */
			services::future<void> continueProcess();

			/**
			 * Pauses the attached process.
			 *
			 * @returns a future object that emits a signal when the process has paused
			 */
			services::future<void> pause();

			/**
			 * Detaches the debugger from the target.
			 * This function is not implemented yet and throws.
			 */
			void detach();

			/**
			 * Shuts down the debugger.
			 */
			void stop();

		protected slots:

			/**
			 * Changes the state of the debugger to the given one.
			 */
			void changeState(Debugger::State::Type state);

			/**
			 * Is called once the process has fully started.
			 * This is also the time when the connection to the process is established.
			 */
			void started();

			/**
			 * Is called when the connection has been closed.
			 */
			void disconnected();

			/**
			 * Is called when there's a problem with the connection.
			 */
			void connection_error(QAbstractSocket::SocketError error);

			/**
			 * Is called when QProcess detects an error (ie. the child process died or something similar).
			 */
			void error(QProcess::ProcessError error);

			/**
			 * Is called when the process has exited.
			 */
			void processFinished(int exitCode, QProcess::ExitStatus status);

		signals:

			void stateChanged();

		private:

			/**
			 * @returns a human readable string for the given error
			 */
			QString toString(QProcess::ProcessError error) const;

			/**
			 * @returns a human readable string for the given error
			 */
			QString toString(QAbstractSocket::SocketError error) const;

		private:

			State::Type                 m_state;
			quint16                     m_port;
			QString                     m_processName;
			logger::memorystream::ptr   m_logger;
			QProcess                    m_process;

			boost::shared_ptr<DebuggerConnection>   m_connection;
			boost::shared_ptr<char>                 m_dummy;
		};
		///////////////////////////////////////////////////////////////////////////////////////////
	}
	///////////////////////////////////////////////////////////////////////////////////////////////
}
///////////////////////////////////////////////////////////////////////////////////////////////////
